/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package snake;

import java.awt.Color;
import java.awt.Graphics;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JPanel;

/**
 *
 * @author Ferenczy Máté
 * @committer Csütörtöki Balázs
 */
public class Terulet extends JPanel implements Serializable{
    private int x=0, y=7;   //x, y: irány
    private int xn, yn;     //xn, yn: kígyó fejének a koordinátái
    private static final int width=301, height=294; //a panel szélessége és magassága
    private boolean pause;  //a játék szüneteltetésének vizsgálata
    private Test food;      //az összeszedendő étel
    private boolean rajta;  //azt viszgálja hogy a food-ot rá rakja-e valamire
    private boolean falnak; //azt vizsgálja hogy falnak ment-e a kígyó
    private Snake snake;    //a főablak változója hogy innen is lehessen a labelt átírni
    private int evett=0;    //hány food-ot evett meg (az új pályára lépéshez)
    private boolean mozgott;    //azt vizsgálja hogy egy mozdulás alatt nem fordult-e kétszer
    private boolean szVolt;     //azt vizsgálja hogy szünet után tért-e vossza aszámoláshoz
    private int seb;        //a kígyó sebessége
    private boolean jobbal=true;    //azt tárolja hogy melyik billentyűket kell figyelnie
    private boolean eszed;  //azt vizsgálja hogy extrát szedett-e
    
    ArrayList<Test> kigyo=new ArrayList<Test>();    //a kígyó testének koordinátáit tárolja
    ArrayList<Fal> fal=new ArrayList<Fal>();        //a falak adatait tároja
    ArrayList<Extra> extrak= new ArrayList<Extra>();   //a felszedhető objektumok
    
    //az ablakon lévő eredmények kiiratásához változók
    private long kIdo;      //a kezdő időt tárolja
    private long eIdo;      //az eltelt időt muataja
    private long perc;      //az eltelt perceket mutatja
    private long mperc;     //az eltelt másodperceket mutatja
    private int vPalya;     //hányadik pályán tart a játékos
    private int vElet;      //hány élrte van még a játékosnak
    private int vPont;      //hány pontja van a játékosnak
    private long elIdo;     //a következő extrát ekkor kell lerakni
    
    public Terulet(Snake snake)
    {
        super();
        this.snake = snake;
        this.setBounds(0, 0, width, height);
        this.setBackground(Color.WHITE);
    }
    
    //a játék kinullázása
    //elso azt tárolja hogy ez az első játék-e, vagy csak előről akarja-e kezdeni a játékos a pályát
    public void ujJatek()
    {
        kigyo.clear();
        fal.clear();
        System.gc();
        
        //változók kinullázása, kígyó, és food elhelyezése
        x=0;
        y=7;
        evett=0;
        kIdo=System.currentTimeMillis();
        eIdo=0;
        elIdo=kIdo+5000;
        vPalya=1;
        vElet=5;
        vPont=0;
        perc=0;
        mperc=0;
        pause=false;
        mozgott=false;
        szVolt=false;
        xn=((width/7)/2)*7;
        yn=((height/7)/2+1)*7;
        seb=100;
        
        kigyo.add(new Test(xn, yn));
        kigyo.add(new Test(xn, yn-7));
        kigyo.add(new Test(xn, yn-14));
        snake.setlIdo(perc, mperc);
        snake.setlPalya(vPalya);
        snake.setlElet(vElet);
        snake.setlPont(vPont);
        
        int fx=0, fy=0;
        rajta=true;
        while(rajta)
        {
            rajta=false;
            fx=(int)((Math.random())*(width/7))*7;
            fy=(int)((Math.random())*(height/7))*7;
            for(int i=0;i < kigyo.size(); i++) 
                if ((kigyo.get(i).getX()==fx) && (kigyo.get(i).getY())==fy) rajta=true;
        }
        food= new Test(fx,fy);
    }
    
    //sebesség beállítása
    public void setSeb(int s)
    {
        seb=s;
    }
    
    //serializált kimentés
    public void mentes(File file) throws IOException
    {
        ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream(file));
        
        //kiíratás a file-ba
        oos.writeObject(kigyo);
        oos.writeObject(x);
        oos.writeObject(y);
        oos.writeObject(xn);
        oos.writeObject(yn);
        oos.writeObject(food);
        oos.writeObject(evett);
        oos.writeObject(eIdo);
        oos.writeObject(vPalya);
        oos.writeObject(vElet);
        oos.writeObject(vPont);
        oos.writeObject(extrak);
        oos.close();
    }
    
    //szerializált olvasás
    public void betolt(File file) throws IOException, ClassNotFoundException
    {
        ObjectInputStream ois=new ObjectInputStream(new FileInputStream(file));
        
        kigyo.clear();  //változók kiürítése
        fal.clear();
        System.gc();
        
        //beolvasás a fájlból
        kigyo=(ArrayList<Test>) ois.readObject();
        x=(Integer) ois.readObject();
        y=(Integer) ois.readObject();
        xn=(Integer) ois.readObject();
        yn=(Integer) ois.readObject();
        food=(Test) ois.readObject();
        evett=(Integer) ois.readObject();
        eIdo=(Long) ois.readObject();
        vPalya=((Integer) ois.readObject())-1;
        vElet=(Integer) ois.readObject();
        vPont=(Integer) ois.readObject();
        extrak=(ArrayList<Extra>) ois.readObject();
        
        //további változók beállítása 
        kIdo=(System.currentTimeMillis()-eIdo);
        ujPalya();
        pause=true;
        snake.setbpauseText("Tovább");
        
        perc=(eIdo/60000)%60;
        mperc=(eIdo/1000)%60;
        snake.setlIdo(perc, mperc);
        snake.setlPalya(vPalya);
        snake.setlElet(vElet);
        snake.setlPont(vPont);
    }
    
    //szünet, folytatás beállítása
    public void setPause(boolean p)
    {
        pause=p;
        if ((vElet==0) || (vPalya==6)) pause=true;
    }
    
    //mozgási billentyűk beállítása
    public void setJobbal(boolean j)
    {
        jobbal=j;
    }
    
    //mozgási irány meghatározása a Snake.java meghívja
    public void mozg(int irany)
    {
        if (irany==32)
        {
            setPause(!pause);
            if (pause) snake.setbpauseText("Tovább");
                else snake.setbpauseText("Szünet");
            this.requestFocus();
            this.repaint();
        }
        if ((!pause) && (!mozgott)) //ha szünet, vagy ebben az "órajelben" mozgott akkor ne mozogjon
        {
            mozgott=true;
            if (jobbal)
            {
                switch(irany)
                {
                    case 37: 
                        if(y==0)
                        {
                            y=-x;
                            x=0;
                        } else
                        {
                            x=y;
                            y=0;
                        }
                        break;
                    case 39: 
                        if (x==0)
                        {
                            x=-y;
                            y=0;
                        } else
                        {
                            y=x;
                            x=0;
                        }
                }
            }else 
            {
                switch (irany)
                {
                    case 37:
                        if (y!=0)
                        {
                            x=-7;
                            y=0;
                        }
                        break;
                    case 38:
                        if (x!=0)
                        {
                            x=0;
                            y=-7;
                        }
                        break;
                    case 39:
                        if (y!=0)
                        {
                            x=7;
                            y=0;
                        }
                        break;
                    case 40:
                        if (x!=0)
                        {
                            x=0;
                            y=7;
                        }
                }
            }
        }
    }
    
    //kezdőértéket állítja be ha meghalt a snake
    //halott=true magába, vagy falba harapott, halott=fale: új pályára lép
    private void kezd(boolean halott)
    {
        if (halott) vElet--;
        snake.setlElet(vElet);
        kigyo.clear();
        System.gc();
        xn=((width/7)/2)*7;
        yn=((height/7)/2+1)*7;
        x=0;
        y=7;
        kigyo.add(new Test(xn, yn));
        kigyo.add(new Test(xn, yn-7));
        kigyo.add(new Test(xn, yn-14));
    }
    
    //a food új helyre helyezése, ahol nincs raja a kígyón, és a kígyó növelése
    private void lerak()
    {
        boolean uj=false;   //azt vizsgálja hogy új pályán van-e
        evett++;
        if(evett==10)
        {
            evett=0;
            kezd(false);
            ujPalya();
            uj=true;
        }
        vPont+=(1000/seb);
        snake.setlPont(vPont);
        int fx=0, fy=0;
        int m=kigyo.size()-1;
        do
        {
            rajta=false;
            fx=(int)((Math.random())*(width/7))*7;
            fy=(int)((Math.random())*(height/7))*7;
            for(int i=0;i < kigyo.size(); i++) 
                if ((kigyo.get(i).getX()==fx) && (kigyo.get(i).getY())==fy) rajta=true;
            if(!fal.isEmpty())
                for(int i=0; i<fal.size(); i++)
                    if((fal.get(i).getX()<=fx) && (fx<fal.get(i).getX()+fal.get(i).getSz()) &&
                            (fal.get(i).getY()<=fy) && (fy<fal.get(i).getY()+fal.get(i).getM()))
                                rajta=true;
        } while(rajta);
        food.mozg(fx, fy);
        
        if (!uj)    //ha nincs új játék akkor növeli a kígyó testét
            kigyo.add(new Test(kigyo.get(m).getX()-(kigyo.get(m).getX()-kigyo.get(m-1).getX())
                    ,kigyo.get(m).getY()-(kigyo.get(m).getY()-kigyo.get(m-1).getY())));
    }
    
    //a falak elhelyezését végzi, ha a játékos új pályára lép
    private void ujPalya()
    {
        fal.clear();
        System.gc();
        vPalya++;
        snake.setlPalya(vPalya);
        switch (vPalya)
        {
            case 2:
            {
                fal.add(new Fal(49, 49, 14, 203));
                fal.add(new Fal(238, 49, 14, 203));
            }
            break;
            case 3:
            {
                fal.add(new Fal(49, 49, 14, 203));
                fal.add(new Fal(238, 49, 14, 203));
                fal.add(new Fal(63, 147, 84, 7));
                fal.add(new Fal(154, 147, 84, 7));
            }
            break;
            case 4:
            {
                fal.add(new Fal(42, 63, 7, 175));
                fal.add(new Fal(252, 63, 7, 175));
                fal.add(new Fal(63, 42, 175, 7));
                fal.add(new Fal(63, 252, 175, 7));
            }
            break;
            case 6:
            case 5:
            {
                fal.add(new Fal(49, 49, 14, 203));
                fal.add(new Fal(238, 49, 14, 203));
                fal.add(new Fal(63, 49, 77, 14));
                fal.add(new Fal(161, 49, 77, 14));
                fal.add(new Fal(63, 238, 77, 14));
                fal.add(new Fal(161, 238, 77, 14));
            }
            break;
        }
                
    }
    
    //extra eltüntetése, és az érték beállítása
    private void extraFelszed(int m)
    {
        eszed=false;
        switch (extrak.get(m).getTipus())
        {
            case 1: //+élet
                snake.setlElet(++vElet);
                break;
            case 2: //+3  testrész
                for (int i=0; i<3; i++)
                        kigyo.add(new Test(kigyo.get(kigyo.size()-1).getX()-(kigyo.get(kigyo.size()-1).getX()-kigyo.get(kigyo.size()-2).getX())
                        ,kigyo.get(kigyo.size()-1).getY()-(kigyo.get(kigyo.size()-1).getY()-kigyo.get(kigyo.size()-2).getY())));
                break;
            case 3: //-3 testrész
                for (int i=0; i<3; i++)
                    if(kigyo.size()>3)
                        kigyo.remove(kigyo.size()-1);
                break;
            case 4: //+pont
            {
                vPont=(int) (vPont+(Math.random()*30)+20);
                snake.setlPont(vPont);
            }
                break;
            case 5: //-pont
            {
                vPont=(int) (vPont-(Math.random()*30)-20);
                if (vPont<0) vPont=0;
                snake.setlPont(vPont);
            }
                break;
            case 6: //-idő
            {
                int i=(int) ((Math.random()*10)+10);
                kIdo=kIdo+i*1000;
                if (System.currentTimeMillis()<kIdo) kIdo=System.currentTimeMillis();
            }
                break;
            case 7: //+idő
            {
                int i=(int) ((Math.random()*10)+10);
                kIdo=kIdo-i*1000;
            }
        }
        extrak.remove(m);
    }
    
    
    private void extraLerak()
    {
        int t=(int) ((Math.random()*6)+1);
        int ex, ey;
        do
        {
            rajta=false;
            ex=(int)((Math.random())*(width/7))*7;
            ey=(int)((Math.random())*(height/7))*7;
            for(int i=0;i < kigyo.size(); i++) //kigyóra rakás vizsgálalta
                if ((kigyo.get(i).getX()==ex) && (kigyo.get(i).getY())==ey) rajta=true;
            if(!fal.isEmpty())  //falra rakás vizsgálata
                for(int i=0; i<fal.size(); i++)
                    if((fal.get(i).getX()<=ex) && (ex<fal.get(i).getX()+fal.get(i).getSz()) &&
                            (fal.get(i).getY()<=ey) && (ey<fal.get(i).getY()+fal.get(i).getM()))
                                rajta=true;
            for(int i=0; i<extrak.size(); i++)  //extrára rakás vozsgálalta
                if((extrak.get(i).getX()==ex) && (extrak.get(i).getY()==ey)) rajta =true;
            if((food.getX()==ex) && (food.getY()==ey)) rajta=true; //kajárarakás vizsgálata
        } while(rajta);
        extrak.add(new Extra(ex, ey, t, System.currentTimeMillis()));
        elIdo=(long) ((Math.random()*10)+5);
        elIdo=(elIdo*1000)+System.currentTimeMillis();
    }
    
    @Override
    public void paintComponent(Graphics g)
    { 
        int melyik=0;
        //ha szünet van, vagy elfogyott az élete, akkor nem mozog ,és nem vizsgál semmit
        if (!pause && vElet>0 && vPalya<6)
        {
            //idő mérése
            if(szVolt)
            {
                szVolt=false;
                kIdo=System.currentTimeMillis()-eIdo;
            }
            eIdo=(System.currentTimeMillis()-kIdo);
            perc=(eIdo/60000)%60;
            mperc=(eIdo/1000)%60;
            snake.setlIdo(perc, mperc);
            if(elIdo-System.currentTimeMillis()<0) extraLerak();
            
            for (int i=extrak.size(); i>0; i--)
                if ((extrak.get(i-1).getKezd()+30000)<System.currentTimeMillis()) extrak.remove(i-1);
            
            xn+=x;  //a kígyó fejének mozgatása
            yn+=y;
            for (int i=kigyo.size()-1; i>0; i--)    //a kígyo testének mozgatása
                kigyo.get(i).mozg(kigyo.get(i-1).getX(), kigyo.get(i-1).getY());
            kigyo.get(0).mozg(xn, yn);  //kígyó fejének beállítása
            
            //azt vizsgálja hogy összeszedett-e extrát
            for (int i=0; i<extrak.size(); i++)
                if ((kigyo.get(0).getX()==extrak.get(i).getX()) && (kigyo.get(0).getY()==extrak.get(i).getY()))
                {
                    eszed=true;
                    melyik=i;
                }
            if (eszed) extraFelszed(melyik);
            
            // azt vizsgálja hogy falnak ment-e
            falnak=false;
            if(!fal.isEmpty())
                for(int i=0; i<fal.size(); i++)
                    if((fal.get(i).getX()<=xn) && (xn<fal.get(i).getX()+fal.get(i).getSz()) &&
                            (fal.get(i).getY()<=yn) && (yn<fal.get(i).getY()+fal.get(i).getM()))
                                falnak=true;

            //magába harapást vizsgálja
            boolean harap=false;
            if (kigyo.size()>4)
                for(int i=4; i<kigyo.size(); i++)
                    if(kigyo.get(0).getX()==kigyo.get(i).getX() && kigyo.get(0).getY()==kigyo.get(i).getY()) 
                        harap=true;

            //azt vizsgálja hogy falnak ment, vagy magába harpott-e, ha igen, akkor középre rakja a kígyót
            if ((xn<0) || (yn<0) || (xn>(width-7)) || (yn>(height-7)) || harap || falnak)
                this.kezd(true);

            //azt vizsgálja hogy a kígyó feje a food-on van-e
            rajta=false;
            for(int i=0;i < kigyo.size(); i++) 
                if ((kigyo.get(i).getX()==food.getX()) && (kigyo.get(i).getY())==food.getY()) rajta=true;
            if (rajta) lerak(); //ha igen akkor lerak egy újat, és növeli a kígyót
        }
        else
            szVolt=true;
        
        //kirajzolás
        super.paintComponent(g);
        g.setColor(Color.GRAY);     //fej
        g.fillOval(kigyo.get(0).getX(), kigyo.get(0).getY(), 7, 7);
        g.setColor(Color.BLACK);    //test
        for (int i=1; i<kigyo.size(); i++)
            g.fillOval(kigyo.get(i).getX(), kigyo.get(i).getY(), 7, 7);

        //food rajzolás
        g.setColor(Color.GREEN);
        g.fillOval(food.getX(), food.getY(), 7, 7);
        
        //extra rajzolás
        g.setColor(Color.RED);    //test
        for (int i=1; i<extrak.size(); i++)
            g.fillOval(extrak.get(i).getX(), extrak.get(i).getY(), 7, 7);
        
        
        //fal rajzolás
        if(!fal.isEmpty())
            for(int i=0; i<fal.size(); i++)
            {
                g.setColor(Color.BLUE);
                g.fillRect(fal.get(i).getX(), fal.get(i).getY(), fal.get(i).getSz(), fal.get(i).getM());
                g.setColor(Color.BLACK);
                g.drawRect(fal.get(i).getX(), fal.get(i).getY(), fal.get(i).getSz(), fal.get(i).getM());
            }
        
        //a játék végének a vizsgálata
        if (!(!pause && vElet>0 && vPalya<6))
        {
            if (vElet==0 && !szVolt) snake.gameOver();
            if (vPalya==6 && !szVolt) try {
                snake.gameEnd(perc, mperc, vPont);
            } catch (IOException ex) {
                Logger.getLogger(Terulet.class.getName()).log(Level.SEVERE, null, ex);
            }
            szVolt=true;
        }
        
        //szüneteltetés
        try {
            Thread.sleep(seb);
        } catch (InterruptedException ex) {
            Logger.getLogger(Terulet.class.getName()).log(Level.SEVERE, null, ex);
        }
        mozgott=false;  //ha mozgott akkor beállítjuk mintha nem mozgott volna
        if (!pause) this.repaint();
    }
}